package com.greenway.beans;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import org.primefaces.event.map.MarkerDragEvent;
import org.primefaces.event.map.PointSelectEvent;
import org.primefaces.json.JSONException;
import org.primefaces.json.JSONObject;
import org.primefaces.model.map.DefaultMapModel;
import org.primefaces.model.map.LatLng;
import org.primefaces.model.map.MapModel;
import org.primefaces.model.map.Marker;
import org.primefaces.model.map.Polygon;
import org.primefaces.model.map.Polyline;

import com.epam.klymanov.pathProcessing.PolutionDose;
import com.epam.klymanov.tools.PathUtility;
import com.epam.klymanov.tools.PollutionMatrix;
import com.epam.klymanov.tools.Polygons;
import com.epam.klymanov.tools.Spot;
import com.epam.klymanov.tools.TypeOfShit;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.greenway.beans.clickstates.AddWaypointState;
import com.greenway.beans.clickstates.FinishMarkerState;
import com.greenway.beans.clickstates.NothingOnMapClickState;
import com.greenway.beans.clickstates.StartMarkerState;
import com.greenway.beans.clickstates.State;
import com.greenway.tools.JsonRoutsReader;
import com.greenway.tools.JsonStepParser;
import com.greenway.tools.Location;
import com.greenway.tools.Step;
import com.greenway.tools.StepDiscret;

public class MapBean {

	private MapModel mapModel;

	private int zoom = 13;
	private List<Step> stepList;
	private List<Location> points;

	private String mode;
	private String stateString;

	private static Integer markerId;

	private Integer distance;
	private Integer duration;

	private State startMarkerState;
	private State finishMarkerState;
	private State addWaypointState;
	private State nothingOnMapClickState;

	private State state;

	public MapBean() {

		mode = "walking";
		stateString = "startFinish";

		markerId = 0;

		mapModel = new DefaultMapModel();

		distance = 0;
		duration = 0;

		startMarkerState = new StartMarkerState(this);
		finishMarkerState = new FinishMarkerState(this);
		addWaypointState = new AddWaypointState(this);
		nothingOnMapClickState = new NothingOnMapClickState(this);

		state = startMarkerState;
	}

	public MapModel getMapModel() {
		return mapModel;
	}

	public void drawRoutByData() throws IOException, JSONException {
		// TODO
		mapModel = new DefaultMapModel();
		drawRout();
		setPollutions();
	}

	public void drawRout() throws IOException, JSONException {
		distance = 0;
		duration = 0;

		// get start-finish markers
		List<Marker> markerList = mapModel.getMarkers();
		Marker markerStart = markerList.get(0);
		Marker markerFinish = markerList.get(1);

		// clear polylines
		mapModel.getPolylines().clear();

		// HTTP
		final Map<String, String> params = Maps.newHashMap();
		params.put("sensor", "false");
		params.put("language", "ru");
		params.put("mode", mode);
		params.put("origin", getMarkerLatlngString(markerStart));
		params.put("destination", getMarkerLatlngString(markerFinish));

		if (markerList.size() > 2) {
			params.put(
					"waypoints",
					getWaypointsString(markerList.subList(2, markerList.size())));
		}

		final JSONObject routs = JsonRoutsReader.read(params);
		stepList = JsonStepParser.getSteps(routs);

		points = StepDiscret.getAllLocations(3, stepList);

		// Polyline
		Polyline polyline = new Polyline();

		polyline.getPaths().add(markerStart.getLatlng());
		LatLng coord3;
		for (Step step : stepList) {
			coord3 = new LatLng(step.getEnd_location().getLat(), step
					.getEnd_location().getLng());
			polyline.getPaths().add(coord3);
			distance += step.getDistance().getValue();
			duration += step.getDuration().getValue();
		}
		polyline.getPaths().add(markerFinish.getLatlng());

		polyline.setStrokeWeight(3);
		polyline.setStrokeColor("#00BF00");
		polyline.setStrokeOpacity(0.7);

		mapModel.addOverlay(polyline);
	}

	public void setPollutions()	throws FileNotFoundException {

		PollutionMatrix pm;
		ArrayList<ArrayList<Spot>> mapAreas = new ArrayList<ArrayList<Spot>>();
		ArrayList<TypeOfShit> diffShit = new ArrayList<TypeOfShit>();

		diffShit.add(new TypeOfShit(0.2, "#00008B", PathUtility
				.getProjectRoot() + "PollutionPolygons.json"));
		diffShit.add(new TypeOfShit(0.1, "#EEAD0E", PathUtility
				.getProjectRoot() + "PollutionPolygonsCO2.json"));
		for (TypeOfShit shit : diffShit) {
			
				Gson gson = new Gson();
				BufferedReader bufferedReader = new BufferedReader(
						new FileReader(shit.getPath()));
				Polygons polygons = gson.fromJson(bufferedReader,
						Polygons.class);

				mapAreas = polygons.getMapAreas();
				pm = polygons.getPollutionMatrix();
				if (mapAreas != null) {
					for (int i = 0; i < mapAreas.size(); i++) {
						if (mapAreas.get(i) != null) {
							Polygon polygon = new Polygon();
							for (int j = 0; j < mapAreas.get(i).size() / 1.23; j++) {
								int latX = mapAreas.get(i).get(j).getX();
								int lonY = mapAreas.get(i).get(j).getY();
								LatLng coord1 = new LatLng(
										pm.getLatitude(lonY),
										pm.getLongitude(latX));
								polygon.getPaths().add(coord1);
							}
							polygon.setStrokeColor(shit.getColor());
							polygon.setFillColor(shit.getColor());
							polygon.setStrokeOpacity(shit.getOpacityLevel());
							polygon.setFillOpacity(shit.getOpacityLevel());
							mapModel.addOverlay(polygon);
						}
					}
				}
		}
	}

	public void deleteMarker(Marker selMarker) throws IOException,
			JSONException {
		// TODO
		mapModel.getMarkers().remove(selMarker);
	}

	public void addMarker(PointSelectEvent event) {

		try {
			state.handleMarker(event, mapModel);
		} catch (IOException | JSONException e) {
			addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO, "Info",
					"ooops something wrong :-((("));
		}

	}

	public void onMarkerDrag(MarkerDragEvent event) {

		try {

			Marker marker = event.getMarker();
			String draggedMarkerId = marker.getId();

			List<Marker> markerList = mapModel.getMarkers();

			for (int i = 0; i < markerList.size(); i++) {
				if (markerList.get(i).getId().equals(draggedMarkerId)) {
					markerList.set(i, marker);
					break;
				}
			}

			drawRout();

		} catch (IOException | JSONException e) {
			addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO, "Info",
					"ooops something wrong :-((("));
		}
	}

	public void reactionOnModeChange() {

		System.out.println("mode = " + mode);
		
		if (!mapModel.getPolylines().isEmpty()) {

			try {
				System.out.println("we have polylines");
				drawRout();
			} catch (IOException | JSONException e) {
				addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO, "Info",
						"ooops something wrong :-((("));
			}
		}
	}

	public void reactionOnStateChange() {

		switch (stateString) {
		case "surf":
			state = nothingOnMapClickState;
			
			try {
				setPollutions();
			} catch (IOException e) {
				addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO, "Info",
						"ooops something wrong :-((("));
			}
			
			break;
		case "waypoints":
			mapModel.getPolygons().clear();
			state = addWaypointState;
			break;
		case "startFinish":
			mapModel.getPolygons().clear();
			mapModel.getMarkers().clear();
			mapModel.getPolylines().clear();
			state = startMarkerState;
			break;
		}

	}

	private String getWaypointsString(List<Marker> markerList) {

		StringBuffer sb = new StringBuffer();

		for (Marker marker : markerList) {
			sb.append(marker.getLatlng().getLat());
			sb.append(",");
			sb.append(marker.getLatlng().getLng());
			sb.append("|");
		}
		sb.deleteCharAt(sb.length() - 1);
		return sb.toString();
	}

	private String getMarkerLatlngString(Marker marker) {
		StringBuffer sb = new StringBuffer();
		sb.append(marker.getLatlng().getLat());
		sb.append(",");
		sb.append(marker.getLatlng().getLng());
		return sb.toString();
	}

	public String getCoordFrom() {
		// return getMarkerLatlngString(markerStart);
		// TODO
		return "need fix";
	}

	public void setCoordFrom(String coordFrom) {
		// TODO
	}

	public String getCoordTo() {
		// return getMarkerLatlngString(markerFinish);
		// TODO
		return "need fix";
	}

	public void setCoordTo(String coordTo) {
		// TODO
	}

	public String getAddressFrom() {
		// return JsonAddressReader.getByCoord(markerStart);
		// TODO
		return "need fix";
	}

	public void setAddressFrom(String address) {
		// if (!address.equals("")) {
		// String add[] = JsonAddressReader.getByAdress(address).split(",");
		//
		// double lat = Double.valueOf(add[0]);
		// double lng = Double.valueOf(add[1]);
		//
		// markerStart = new Marker(new LatLng(lat, lng), "Start", null,
		// "http://localhost:9090/GreenPlanet/images/start.png");
		// }
		// TODO
	}

	public String getAddressTo() {
		// return JsonAddressReader.getByCoord(markerFinish);
		// TODO
		return "need fix";
	}

	public void setAddressTo(String address) {
		// TODO
		// if (!address.equals("")) {
		// String add[] = JsonAddressReader.getByAdress(address).split(",");
		//
		// double lat = Double.valueOf(add[0]);
		// double lng = Double.valueOf(add[1]);
		//
		// markerFinish = new Marker(new LatLng(lat, lng), "Start", null,
		// "http://localhost:9090/GreenPlanet/images/start.png");
		//
		// }
	}

	public String getCenter() {
		List<Marker> markerList = mapModel.getMarkers();

		if (markerList.size() < 2) {
			return 50.431466 + "," + 30.487627;
		} else {

			StringBuffer sb = new StringBuffer();

			Marker markerStart = markerList.get(0);
			Marker markerFinish = markerList.get(1);

			sb.append((markerStart.getLatlng().getLat() + markerFinish
					.getLatlng().getLat()) / 2);
			sb.append(",");
			sb.append((markerStart.getLatlng().getLng() + markerFinish
					.getLatlng().getLng()) / 2);

			return sb.toString();
		}
	}

	public List<Marker> getMarkerList() {
		return mapModel.getMarkers();
	}

	public ArrayList<PolutionDose> getShitTable() {
		// if (markerFinish.getLatlng().getLat() == 0.0) {
		// return new ArrayList<PolutionDose>();
		// } else {
		// return ShitCounter.ShitDose(points);
		// }
		// TODO
		return new ArrayList<PolutionDose>();
	}

	public void addMessage(FacesMessage message) {
		FacesContext.getCurrentInstance().addMessage(null, message);
	}

	public int getZoom() {
		return zoom;
	}

	public Integer getDistance() {
		return distance;
	}

	public Integer getDuration() {
		return (int) duration / 60;
	}

	public String getMode() {
		return mode;
	}

	public void setMode(String mode) {
		this.mode = mode;
	}

	public String getStateString() {
		return stateString;
	}

	public void setStateString(String stateString) {
		this.stateString = stateString;
	}

	public List<Step> getStepList() {
		return stepList;
	}

	public State getStartMarkerState() {
		return startMarkerState;
	}

	public State getFinishMarkerState() {
		return finishMarkerState;
	}

	public State getNothingOnMapClickState() {
		return nothingOnMapClickState;
	}

	public State getAddWaypointState() {
		return addWaypointState;
	}

	public void setState(State state) {
		this.state = state;
	}

	public Integer getMarkerId() {
		return markerId;
	}

	public void setMarkerId(Integer markerId) {
		MapBean.markerId = markerId;
	}

}